package com.g.places.api.base;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;

import com.g.places.api.execptions.GPlaceResponseStatusException;
import com.g.places.api.model.GBasePlace;
import com.g.places.api.model.GPlace;
import com.g.places.api.util.GPlacesUtil;
import com.g.places.api.util.Parameter;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * Basic functionality to fetch data from google places currently we implement
 * connection using apche HTTP Client with only JSON output type object that
 * fetching
 * <ul>
 * <li>
 * Place Search Requests by text searching </li>
 * <li>
 * Place Details by reference</li>
 * </ul>
 * 
 * @author ahmad
 * 
 */
public abstract class AbstractPlaceClient implements PlacesClient {

	
	// connection made by HTTP client
	private HttpClient httpClient;
	
	// JSON mapping to object
	// @see FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES
	private Gson gson = new GsonBuilder().setFieldNamingPolicy(
			FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();

	/**
	 * Default constructor using httpClient to make connection
	 * 
	 * @param httpClient
	 *            - http client
	 */
	public AbstractPlaceClient(HttpClient httpClient) {

		this.httpClient = httpClient;

	}

	/**
	 * 
	 * fetching google places data by given base URI ,output type, required
	 * parameters with result object type
	 * 
	 * @param baseUrl
	 *            base URL to gather Places from google places API
	 * @param outputType
	 *            ENUM represent output type either JSON / XML
	 * @param objectType
	 *            class type for result @See {@link GPlace}
	 * @param parameters
	 *            request parameters to add to URL
	 * 
	 * @return fetched data from Google Places the result object must match
	 *         given URL
	 * @return objects extends GBasePlace according to given url otherwise null
	 * @throws GPlaceResponseStatusException
	 *             when occurred some error while get response
	 */
	@Override
	public <T extends GBasePlace> T fetchGPlaces(String baseUrl,
			OutputType outputType, Class<T> objectType, Parameter... parameters) {

		// currently JSON only
		if (outputType != null && outputType.equals(OutputType.JSON)) {

			// create URL by given base URL ,JSON , parameters
			String url = GPlacesUtil.defaultGPlacesUrlBuilder(baseUrl,
					outputType, parameters);

			// fetch Places matching given objectType
			T gPlace = fetchPlaces(url, objectType);

			// get response status if it's not OK or zero_results throw status response exception
			if (!gPlace.getStatus().equals(StatusResponse.OK)
					&& !gPlace.getStatus().equals(
							StatusResponse.ZERO_RESULTS.name())) {
					// throw exception
				/*throw new GPlaceResponseStatusException("status response exception , status is !"
						+ gPlace.getStatus());*/
				
				// ADD LOGGER 
				
			}

			
			return gPlace;
		}
		
		// return null on output type given is not json or on any error occurred 
		return null;
	}

	
	/**
	 * 
	 * @param url  full URL represent web service for fetching google places objects
	 * @param objectType represent fetching object type 
	 * @return object represent places/place object as given object 
	 * NOTE : THIS METHOD MAKE HTTP REQUEST WITH HEADER - JSON/APPLICATION FOR CURRENTLY VERSION
	 */
	private <T extends GBasePlace> T fetchPlaces(String url, Class<T> objectType) {

		try {
			
			// create get http request  
			HttpUriRequest hur = new HttpGet(url);
			
			// adding json header ! for current version we only fetch json data !!!!!!! 
			hur.addHeader("accept", "application/json"); 
			
			// execute request and get http response 
			HttpResponse response = httpClient.execute(hur);
			
			// http entity 
			HttpEntity entity = response.getEntity();
			
			// get response content 
			InputStream instream = entity.getContent();

			try {
				// reader 
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(instream));

				//  convert JSON to object type 
				return gson.fromJson(reader, objectType);

			} catch (RuntimeException ex) {

				hur.abort();
				throw ex;

			} finally {

				// Closing the input stream will trigger connection release
				instream.close();

			}

		} catch (Exception e) {

		} finally {
			
			
			// Release the connection.
		}
		return null;

	}

	public HttpClient getHttpClient() {
		return httpClient;
	}

	public void setHttpClient(HttpClient httpClient) {
		this.httpClient = httpClient;
	}

}
